本文是閱讀有關 Angular 的元件生命週期的 OnChanges
的筆記內容。
呼叫時機: 當元件中的 @Input
類型的屬性,發生變化的時候,這個 lifecycle hook 就會被呼叫。
例如:
[TypeScript]
import { OnChanges, SimpleChanges, Input} from '@angular/core';
export class AppComponent implements OnChanges {
@Input() book;
ngOnChanges(changes: SimpleChanges) {
console.log(this.book);
}
}
上面的範例中,可以看到在這個元件裡面定義了一個 @Input 的屬性 book,每當 book 的內容有變化的時候,就會觸發到 ngOnChanges
的 hook 喔。
而 ngOnChanges
會自帶一個參數,他是一個物件,裡面會包含的內容為 @Input 屬性的現在的值和上一次的值。
這邊我們先來寫個 @Input
和 ngOnChanges
的範例
[子元件 - TypeScript]
@Component({
selector: 'bcomp',
template: `
<div *ngFor="let user of users">
{{ user }}
</div>
`
})
export class BComponent implements OnChanges {
@Input() users
ngOnChanges() {
console.log("changed")
}
}
[父元件 - TypeScript]
@Component({
template: `
<bcomp [users]="users"></bcomp>
`
})
export class App {
users = ["Jasper", "Tom", "Mary"]
}
可以看到在子元件定義了一個 @Input property 叫做 users,並把這個屬性綁到父元件的裡面,去接收來自父元件的 users 內容。
上面的內容,會呈現如下的畫面
就很單純的把資料渲染到畫面上。
那如果今天當點擊某個按鈕,會逐一刪除 users 裡面的內容,那承如上面所講過的當 @Input 屬性的內容改變的時候,照道理講子元件的 ngOnChanges 這個 hook 會被觸發。
那來改寫一下上面的範例吧
[子元件 - TypeScript]
export class BComponent implements OnInit, OnChanges {
@Input() users;
ngOnChanges(obj: SimpleChanges) {
console.log(obj);
}
}
[父元件 - TypeScript]
@Component({
template: `
<app-b [users]="users"></app-b>
<button (click)="delete()">delete</button>
`,
})
export class AppComponent {
users = ['Jasper', 'Tom', 'Mary'];
delete() {
this.users.splice(0, 1);
}
}
可以看到上面的範例,在父元件有個 delete 的函式,每當點擊按鈕就會觸發它,並刪除 users 的陣列的內容。
操作畫面如下
可以看到畫面上的陣列內容,確實地逐一被刪除,但是, console.log 中並沒有觸發子元件的 ngOnChanges 裡面的內容,將改變的內容印出來。
原因是什麼呢?
因為,users 陣列透過 splice 來刪除它的陣列內容,但並沒有改變它陣列的位址,所以,對 @Input
屬性 users 來說,它都是指向同一個位址的陣列,儘管它內容有被刪減,對 @Input 屬性 users 來講還是同一個人根本沒變化,所以,才沒有觸發它的 ngOnChanges 內容囉。
那要怎麼改呢?
很簡單,我們就用那種會回傳新陣列的刪除方法,也就是 slice
。
那我們來改寫一下,父元件的內容
[父元件 - TypeScript]
@Component({
template: `
<app-b [users]="users"></app-b>
<button (click)="delete()">delete</button>
`,
})
export class AppComponent {
users = ['Jasper', 'Tom', 'Mary'];
delete() {
this.users = this.users.slice(1);
}
}
你可以看到,我們把經過 slice 刪除後產生的陣列回傳給原本的 users,讓它去指向另一個位址的新陣列。
操作結果如下
可以看到子元件的 ngOnChanges 函式就會被觸發囉,而且我們可以透過印出 ngOnChanges 參數內容,去看每一次子元件的 users 陣列內容是真的也被刪除掉了。
來做個總結
ngOnChanges
會在元件的 @Input
屬性有改變的時候,被觸發。@Input
內容的傳址問題,會導致子元件的 ngOnChanges
不會被觸發,若要改變這種現象的話,就必須使用 non-mutating method (也就是會回傳新的物件的方式) 來達成。